package org.hawk.gwt.ppc.introspection.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;

import org.hawk.gwt.ppc.introspection.JavaScope;
import org.hawk.gwt.ppc.introspection.JavaTypeScope;
import org.hawk.gwt.ppc.introspection.JavaTypeVariable;
import org.hawk.gwt.ppc.utils.Name;

/**
 * Represents java type variable introspection type.
 * 
 * @author alex.bereznevatiy@gmail.com
 *
 */
final class JavaTypeVariableImpl extends AbstractJavaGenericTypeScope implements JavaTypeVariable {
	
	private String name;
	private List<String> bounds;
	private List<JavaTypeScope> resolvedBounds;

	JavaTypeVariableImpl(String name, List<String> bounds, JavaTypeScope raw, JavaScope owner) {
		super(raw, owner);
		this.name = name;
		this.bounds = bounds;
	}
	
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.impl.AbstractJavaGenericTypeScope#getName()
	 */
	public String getName() {
		return name;
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.introspection.JavaTypeVariable#getBounds()
	 */
	public List<JavaTypeScope> getBounds() {
		if (resolvedBounds == null) {
			resolveBounds();
		}
		return resolvedBounds;
	}
	

	private void resolveBounds() {
		ListIterator<String> iterator = bounds.listIterator();
		List<JavaTypeScope> result = new ArrayList<JavaTypeScope>(bounds.size());
		while(iterator.hasNext()) {
			String name = iterator.next();
			JavaTypeScope type = ((JavaScopeImpl)getOwner()).resolveType(name, getCompilationUnit());
			String params = Name.getTypeParameters(name);
			
			if (params.isEmpty()) {
				result.add(type);
			} else if (params.endsWith("[]")) {
				result.add(resolveArrayType(type, params));
			} else {
				result.add(new JavaParameterizedGenericTypeScopeImpl(type, getOwner(), params));
			}
		}
		resolvedBounds = Collections.unmodifiableList(result);
	}
	
	JavaTypeScope getRaw() {
		JavaTypeScope raw = super.getRaw();
		if (raw != null) {
			return raw;
		}
		if (resolvedBounds == null) {
			resolveBounds();
		}
		return resolvedBounds.get(0);
	}

	private JavaTypeScope resolveArrayType(JavaTypeScope type, String params) {
		params = params.substring(0, params.length() - 2);
		if (params.endsWith("[]")) {
			return new JavaArrayGenericTypeScopeImpl(resolveArrayType(type, params), getOwner());
		}
		JavaTypeScope component = new JavaParameterizedGenericTypeScopeImpl(type, getOwner(), params);
		return new JavaArrayGenericTypeScopeImpl(component, getOwner());
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return getName();
	}
}
